package ferp.android.services;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import ferp.core.ai.tree2.Resolver;
import ferp.core.log.Log;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * User: igorgok
 * Date: 4/8/12 10:38 AM
 */
public class ResolverService2 extends Service
{
  public static final int OK                        =  0;
  public static final int ASSERTION_ERROR           = -1;
  public static final int NULL_POINTER_EXCEPTION    = -2;
  public static final int ARRAY_INDEX_OUT_OF_BOUNDS = -3;
  public static final int OUT_OF_MEMORY             = -4;
  public static final int THROWABLE                 = -5;

  @Override
  public void onCreate()
  {
    super.onCreate();

    Log.debug(TAG, "Resolver service created " + android.os.Process.myPid());
  }

  @Override
  public void onStart(Intent intent, int startId)
  {
    super.onStart(intent, startId);

    exception = null;
    Resolver.ready = false;

    Log.debug(TAG, "Resolver service started");
  }

  /*
  @Override
  public int onStartCommand(Intent intent, int flags, int startId)
  {
    int ret = super.onStartCommand(intent, flags, startId);
    Log.debug(Log.TAG, "sticky: " + ret);
    return ret;
  }
  */

  @Override
  public void onDestroy()
  {
    Log.debug(TAG, "Resolver service destroyed");

    super.onDestroy();
  }

  @Override
  public void onLowMemory()
  {
    Log.debug(TAG, "*** Resolver service low memory ***");

    super.onLowMemory();
  }

  public IBinder onBind(Intent intent)
  {
    Log.debug(TAG, "Resolver service bound");

    exception = null;
    Resolver.ready = false;

    return binder;
  }

  @Override
  public boolean onUnbind(Intent intent)
  {
    Log.debug(TAG, "Resolver service unbound");

    return super.onUnbind(intent);
  }

  @Override
  public void onRebind(Intent intent)
  {
    Log.debug(TAG, "Resolver service rebound");

    super.onRebind(intent);
  }

  private final IResolverService2.Stub binder = new IResolverService2.Stub()
  {
    @Override
    public byte preallocate() throws RemoteException
    {
      Log.debug(TAG, "Resolver service preallocating");

      try
      {
        Resolver.preallocate();

        return OK;
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public byte initialize(int evaluator, int declarer, int starter, int trump, int hand0, int hand1, int hand2, byte talon0, byte talon1, int dropReal, int dropGuessed, int round, boolean rostov, boolean open) throws RemoteException
    {
      Log.debug(TAG, "Resolver service initializing");

      try
      {
        Resolver.initialize(evaluator, declarer, starter, trump, hand0, hand1, hand2, talon0, talon1, dropReal, dropGuessed, round, rostov, open);

        return OK;
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public byte recalculateOnNextSolve(int hand0, int hand1, int hand2, int trick) throws RemoteException
    {
      Log.debug(TAG, "Resolver service changed for standing game");

      try
      {
        Resolver.recalculateOnNextSolve(hand0, hand1, hand2, trick);

        return OK;
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public byte solve() throws RemoteException
    {
//    For debugging situation, when service process has been killed
/*
      if (true)
      {
        Log.debug(TAG, "Killing resolver process");
        android.os.Process.killProcess(android.os.Process.myPid());
      }
*/

      Log.debug(TAG, "Resolver service solving");

      try
      {
        Resolver.solve();

        return OK;
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public byte getBestMove(int level, int hand, int trick, int cardsInHand) throws RemoteException
    {
      Log.debug(TAG, "Resolver service getting best move");

      try
      {
        return Resolver.getBestMove(level, hand, trick, cardsInHand);
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public int getBestScore() throws RemoteException
    {
      Log.debug(TAG, "Resolver service getting best score");

      try
      {
        return Resolver.getBestScore();
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public byte advance(int hand, int trick) throws RemoteException
    {
      Log.debug(TAG, "Resolver service advancing");

      try
      {
        Resolver.advance(hand, trick);

        return OK;
      }
      catch (Throwable e)
      {
        return report(e);
      }
    }

    @Override
    public long getAvailableMemory() throws RemoteException
    {
      try
      {
        return Resolver.getAvailableMemory();
      }
      catch (Exception e)
      {
        return report(e);
      }
    }

    @Override
    public int getNodePoolCapacity() throws RemoteException
    {
      try
      {
        return Resolver.getNodePoolCapacity();
      }
      catch (Exception e)
      {
        return report(e);
      }
    }

    @Override
    public int getMovePoolCapacity() throws RemoteException
    {
      try
      {
        return Resolver.getMovePoolCapacity();
      }
      catch (Exception e)
      {
        return report(e);
      }
    }

    @Override
    public int getSpeed() throws RemoteException
    {
      try
      {
        return Resolver.getSpeed();
      }
      catch (Exception e)
      {
        return report(e);
      }
    }

    @Override
    public String getExceptionTrace() throws RemoteException
    {
      if (exception == null)
        return "no service stack trace";

      StringWriter sw = new StringWriter(1024);
      PrintWriter pw = new PrintWriter(sw);

      exception.printStackTrace(pw);
      exception = null;

      return sw.toString();
    }
  };

  private static final String TAG = Log.TAG + "/S2";
  private static Throwable exception;

  private byte report(Throwable e)
  {
    exception = e;

    if (e instanceof AssertionError)
      return ASSERTION_ERROR;

    if (e instanceof NullPointerException)
      return NULL_POINTER_EXCEPTION;

    if (e instanceof ArrayIndexOutOfBoundsException)
      return ARRAY_INDEX_OUT_OF_BOUNDS;

    if (e instanceof OutOfMemoryError)
      return OUT_OF_MEMORY;

    return THROWABLE;
  }
}
